import tkinter as tk
from tkinter import filedialog, messagebox, colorchooser
import ttkbootstrap as ttk
from PIL import Image, ImageFilter, ImageChops, ImageTk
import os
from collections import Counter
import numpy as np
import cv2
import threading
from tkinter import ttk as tk_ttk
from detector_color import distance_alpha  # 新增
from detector_portrait import floodfill_alpha  # 新增


class TransparencyTool:
    def __init__(self, root):
        self.root = root
        self.root.title("图片背景透明化工具")

        # 绑定窗口大小变化事件
        self.root.bind("<Configure>", self.on_window_resize)

        # 创建主题并设置颜色
        self.style = ttk.Style(theme="flatly")

        # 修改主题颜色
        self.style.colors.primary = "#FF9F43"
        self.style.colors.secondary = "#FF9F43"
        self.style.colors.success = "#FF9F43"
        self.style.colors.info = "#FF9F43"
        self.style.colors.warning = "#FF9F43"
        self.style.colors.danger = "#FF9F43"

        # 设置全局颜色
        self.style.configure(".",
                             background="white",
                             foreground="black")

        # 创建主框架
        self.main_frame = ttk.Frame(self.root, padding="20")
        self.main_frame.pack(fill=tk.BOTH, expand=True)

        # 配置主框架的行列权重，使内部组件可以随窗口大小调整
        self.main_frame.columnconfigure(0, weight=1)

        # 创建内容框架
        self.content_frame = ttk.Frame(self.main_frame)
        self.content_frame.pack(fill=tk.BOTH, expand=True)

        # 配置内容框架的行列权重
        self.content_frame.columnconfigure(0, weight=1)

        # 文件选择区域
        self.source_frame = ttk.LabelFrame(self.content_frame, text="选择图片", padding="10")
        self.source_frame.pack(fill=tk.X, pady=(0, 10))

        self.select_btn = ttk.Button(
            self.source_frame,
            text="浏览",
            command=self.select_image,
            bootstyle="danger"
        )
        self.select_btn.pack(side=tk.RIGHT, pady=10)

        # 修改文件标签，使用Treeview显示文件
        self.file_label_frame = ttk.Frame(self.source_frame)
        self.file_label_frame.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10), pady=10)

        self.file_listbox = ttk.Treeview(self.file_label_frame, columns=("path",), show="", height=1)
        self.file_listbox.pack(fill=tk.X, expand=True)
        self.file_listbox.insert("", "end", values=("未选择文件",))

        # 参数设置区域
        settings_frame = ttk.LabelFrame(self.content_frame, text="参数设置", padding="10")
        settings_frame.pack(fill=tk.X, pady=(0, 10))

        # 容差值设置
        tolerance_frame = ttk.Frame(settings_frame)
        tolerance_frame.pack(fill=tk.X, pady=5)

        # 添加说明标签
        ttk.Label(
            settings_frame,
            text="提示：数值越大，越多相似的背景色会被去除 (比如背景是渐进色)\n          数值越小越适合去除完全纯色的背景",
            foreground='#666666'
        ).pack(anchor='w', pady=(0, 5))

        ttk.Label(tolerance_frame, text="背景去除程度:").pack(side=tk.LEFT, pady=5)

        # 添加数值显示标签
        self.tolerance_value_label = ttk.Label(tolerance_frame, text="30", width=3)
        self.tolerance_value_label.pack(side=tk.LEFT, padx=(5, 0), pady=5)

        self.tolerance = ttk.Scale(
            tolerance_frame,
            from_=0,
            to=100,
            orient=tk.HORIZONTAL,
            length=300,
            command=self.update_tolerance_value,
            bootstyle="danger"
        )
        self.tolerance.set(30)  # 默认值
        self.tolerance.pack(side=tk.LEFT, padx=10, fill=tk.X, expand=True, pady=5)

        # 添加边缘平滑度设置
        feather_frame = ttk.Frame(settings_frame)
        feather_frame.pack(fill=tk.X, pady=5)

        ttk.Label(feather_frame, text="边缘柔和度:").pack(side=tk.LEFT, pady=5)

        # 添加羽化数值显示标签
        self.feather_value_label = ttk.Label(feather_frame, text="5", width=3)
        self.feather_value_label.pack(side=tk.LEFT, padx=(5, 0), pady=5)

        self.feather = ttk.Scale(
            feather_frame,
            from_=0,
            to=20,
            orient=tk.HORIZONTAL,
            length=300,
            command=self.update_feather_value,
            bootstyle="danger"
        )
        self.feather.set(2)  # 默认值
        self.feather.pack(side=tk.LEFT, padx=10, fill=tk.X, expand=True, pady=5)

        # 添加边缘柔和度的说明
        ttk.Label(
            settings_frame,
            text="提示：数值越大，边缘过渡越柔和自然 (类似PS的背景透明效果)\n          数值越小边缘越锐利",
            foreground='#666666'
        ).pack(anchor='w', pady=(0, 5))

        # 添加边缘清晰度设置
        edge_frame = ttk.Frame(settings_frame)
        edge_frame.pack(fill=tk.X, pady=5)

        ttk.Label(edge_frame, text="边缘精细度:").pack(side=tk.LEFT, pady=5)

        # 添加清晰度数值显示标签
        self.edge_value_label = ttk.Label(edge_frame, text="7", width=3)
        self.edge_value_label.pack(side=tk.LEFT, padx=(5, 0), pady=5)

        self.edge_clarity = ttk.Scale(
            edge_frame,
            from_=1,
            to=15,
            orient=tk.HORIZONTAL,
            length=300,
            command=self.update_edge_value,
            bootstyle="danger"
        )
        self.edge_clarity.set(2)  # 默认值
        self.edge_clarity.pack(side=tk.LEFT, padx=10, fill=tk.X, expand=True, pady=5)

        # 添加边缘精细度的说明
        ttk.Label(
            settings_frame,
            text="提示：数值越大，越能保留头发丝等细节 (但可能会有些噪点)\n          数值越小边缘越平滑但可能丢失细节",
            foreground='#666666'
        ).pack(anchor='w', pady=(0, 5))

        # ==================== 新增：输出背景选项 ====================
        bg_frame = ttk.Frame(settings_frame)
        bg_frame.pack(fill=tk.X, pady=5)

        ttk.Label(bg_frame, text="输出背景:").pack(side=tk.LEFT, pady=5)

        self.bg_mode_var = tk.StringVar(value="transparent")  # transparent / solid / image
        self.bg_color = (255, 255, 255)  # 纯色背景默认白色
        self.bg_image_path = None  # 自定义背景图路径

        ttk.Radiobutton(bg_frame, text="透明", variable=self.bg_mode_var, value="transparent",
                        command=self.on_bg_mode_change).pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(bg_frame, text="纯色", variable=self.bg_mode_var, value="solid",
                        command=self.on_bg_mode_change).pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(bg_frame, text="图片", variable=self.bg_mode_var, value="image",
                        command=self.on_bg_mode_change).pack(side=tk.LEFT, padx=5)

        # ---------- 同行加入：背景识别方式 & 取样按钮 ----------
        self.detect_frame = ttk.Frame(bg_frame)
        self.detect_frame.pack(side=tk.LEFT, padx=(20, 0))

        ttk.Label(self.detect_frame, text="背景识别:").pack(side=tk.LEFT, pady=5)

        self.detect_mode_var = tk.StringVar(value="auto")  # auto / sample
        ttk.Radiobutton(self.detect_frame, text="自动", variable=self.detect_mode_var, value="auto",
                        command=self.on_detect_mode_change).pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(self.detect_frame, text="取样", variable=self.detect_mode_var, value="sample",
                        command=self.on_detect_mode_change).pack(side=tk.LEFT, padx=5)

        self.pick_color_btn = ttk.Button(self.detect_frame, text="取样颜色", command=self.open_color_picker,
                                         bootstyle="secondary", state=tk.DISABLED)
        self.pick_color_btn.pack(side=tk.LEFT, padx=5)

        # 重置取样按钮
        self.reset_sample_btn = ttk.Button(self.detect_frame, text="重置取样", command=self.reset_samples,
                                           bootstyle="warning")
        self.reset_sample_btn.pack(side=tk.LEFT, padx=5)

        # ------------------- 新增：算法选择 -------------------
        self.alg_mode_var = tk.StringVar(value="color")
        ttk.Label(self.detect_frame, text="算法:").pack(side=tk.LEFT, padx=(20, 0))
        ttk.Radiobutton(self.detect_frame, text="颜色距离", variable=self.alg_mode_var, value="color").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(self.detect_frame, text="洪水Trimap", variable=self.alg_mode_var, value="flood").pack(side=tk.LEFT, padx=5)
        # ----------------------------------------------------

        # 按需显示的按钮（选择纯色/图片背景）
        self.choose_color_btn = ttk.Button(bg_frame, text="选择颜色", command=self.choose_bg_color, bootstyle="secondary")
        self.sample_bg_btn = ttk.Button(bg_frame, text="取样背景色", command=self.sample_bg_color, bootstyle="secondary")
        self.choose_image_btn = ttk.Button(bg_frame, text="选择图片", command=self.choose_bg_image, bootstyle="secondary")
        # 默认全部隐藏
        self.choose_color_btn.pack_forget()
        self.sample_bg_btn.pack_forget()
        self.choose_image_btn.pack_forget()
        # ==========================================================

        # 自动裁剪透明边开关
        self.auto_crop_var = tk.BooleanVar(value=False)
        ttk.Checkbutton(settings_frame, text="处理后裁剪透明边", variable=self.auto_crop_var,
                        bootstyle="success").pack(anchor='w', pady=(0, 5))

        # 处理按钮和进度条
        button_frame = ttk.Frame(self.content_frame)
        button_frame.pack(fill=tk.X, pady=10)

        # 使用grid布局来确保按钮居中
        button_frame.columnconfigure(0, weight=1)
        button_frame.columnconfigure(2, weight=1)

        self.process_btn = ttk.Button(
            button_frame,
            text="开始处理",
            command=self.process_image,
            bootstyle="danger",
            state=tk.DISABLED,
            width=15
        )
        # 将按钮放在中间列
        self.process_btn.grid(row=0, column=1, pady=10)

        # 添加进度条
        self.progress_frame = ttk.Frame(self.content_frame)
        self.progress_frame.pack(fill=tk.X, pady=(0, 10))
        
        self.progress_label = ttk.Label(self.progress_frame, text="")
        self.progress_label.pack(pady=(0, 5))
        
        self.progress_bar = tk_ttk.Progressbar(
            self.progress_frame, 
            mode='indeterminate',
            style="Horizontal.TProgressbar"
        )
        self.progress_bar.pack(fill=tk.X, padx=20)
        
        # 初始隐藏进度条
        self.progress_frame.pack_forget()

        # 保存单个图片时使用，兼容旧逻辑
        self.image_path = None
        # 新增：支持多选图片批量处理
        self.image_paths = []
        # 批量保存目录（仅多图时使用）
        self.batch_save_dir = None

        # 手动取样的背景颜色列表
        self.manual_bg_colors = []

    def select_image(self):
        # 使用 askopenfilenames 允许多选
        file_paths = filedialog.askopenfilenames(
            title="选择图片文件",
            filetypes=[
                ("常见图片格式", "*.png *.jpg *.jpeg *.bmp *.gif *.tiff *.webp"),
                ("PNG文件", "*.png"),
                ("JPEG文件", "*.jpg *.jpeg"),
                ("BMP文件", "*.bmp"),
                ("GIF文件", "*.gif"),
                ("TIFF文件", "*.tiff *.tif"),
                ("WebP文件", "*.webp"),
                ("所有文件", "*.*")
            ]
        )
        if file_paths:
            # 转为列表以便后续处理
            self.image_paths = list(file_paths)
            # 为兼容单图逻辑，如果只选择了一张图，则仍然设置 image_path
            self.image_path = self.image_paths[0] if len(self.image_paths) == 1 else None

            # 截断过长的文件路径，只显示文件名和部分路径
            # 更新文件列表显示
            self.file_listbox.configure(height=min(10, len(self.image_paths)))

            # 清空现有项目
            for item in self.file_listbox.get_children():
                self.file_listbox.delete(item)

            # 添加所有选中的文件
            for p in self.image_paths:
                display_path = p
                if len(p) > 50:
                    filename = os.path.basename(p)
                    directory = os.path.dirname(p)
                    display_path = f"...{directory[-20:]}\\{filename}" if directory else filename
                self.file_listbox.insert("", "end", values=(display_path,))

            self.process_btn.config(state=tk.NORMAL)
            # 根据识别模式决定取样按钮状态
            self.on_detect_mode_change()

    def show_progress(self, message="处理中..."):
        """显示进度条"""
        self.progress_label.config(text=message)
        self.progress_frame.pack(fill=tk.X, pady=(0, 10))
        self.progress_bar.start(10)
        self.process_btn.config(state=tk.DISABLED)
        self.root.update()

    def hide_progress(self):
        """隐藏进度条"""
        self.progress_bar.stop()
        self.progress_frame.pack_forget()
        self.process_btn.config(state=tk.NORMAL)
        self.root.update()

    def process_image(self):
        # 如果未选择图片则返回
        if not self.image_paths:
            return

        # 如果是多张，先让用户选择统一输出文件夹
        if len(self.image_paths) > 1:
            dir_path = filedialog.askdirectory(title="选择批量输出文件夹")
            if not dir_path:
                return  # 用户取消
            self.batch_save_dir = dir_path
        else:
            self.batch_save_dir = None

        # 在新线程中批量处理图片，避免界面卡死
        threading.Thread(target=self._process_images_thread, daemon=True).start()

    def _process_single_image(self, img_path, idx=1, total=1):
        """在后台线程中处理图片"""
        try:
            prefix = f"({idx}/{total}) " if total > 1 else ""
            # 显示进度条
            self.root.after(0, lambda: self.show_progress(f"{prefix}正在读取图片..."))

            # 解决中文路径问题 - 使用numpy和cv2的组合方式读取
            try:
                # 使用numpy读取文件数据，然后用cv2解码
                with open(img_path, 'rb') as f:
                    img_data = np.frombuffer(f.read(), np.uint8)
                original_img = cv2.imdecode(img_data, cv2.IMREAD_COLOR)
            except Exception as read_error:
                self.root.after(0, lambda: messagebox.showerror("错误", f"无法读取图片文件：{str(read_error)}"))
                return

            # 检查图片是否成功加载
            if original_img is None:
                self.root.after(0, lambda: messagebox.showerror("错误", "无法读取图片文件，可能是不支持的格式或文件已损坏"))
                return

            # 转换BGR到RGB
            rgb_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2RGB)

            # 获取图片尺寸
            height, width = rgb_img.shape[:2]

            self.root.after(0, lambda: self.show_progress(f"{prefix}正在分析背景颜色..."))

            # 收集边缘像素
            edge_pixels = []

            # 获取四个边的像素
            edge_pixels.extend(rgb_img[0, :, :].reshape(-1, 3))  # 上边缘
            edge_pixels.extend(rgb_img[height - 1, :, :].reshape(-1, 3))  # 下边缘  
            edge_pixels.extend(rgb_img[:, 0, :].reshape(-1, 3))  # 左边缘
            edge_pixels.extend(rgb_img[:, width - 1, :].reshape(-1, 3))  # 右边缘

            # 使用Counter统计边缘颜色频率
            edge_pixels = [tuple(pixel) for pixel in edge_pixels]
            color_counts = Counter(edge_pixels)
            total_edge_pixels = len(edge_pixels)

            # 获取所有出现频率超过阈值的颜色作为背景色
            threshold_percentage = 1
            background_colors = []

            for color, count in color_counts.items():
                if (count / total_edge_pixels) * 100 >= threshold_percentage:
                    background_colors.append(color)

            # 如果没有颜色超过阈值，则使用最常见的颜色
            if not background_colors:
                background_colors = [color_counts.most_common(1)[0][0]]

            # 如果处于取样模式，则替换为手动颜色
            if self.detect_mode_var.get() == "sample":
                if self.manual_bg_colors:
                    background_colors = self.manual_bg_colors
                else:
                    self.root.after(0, lambda: self._handle_error("请先取样背景颜色"))
                    return

            # 获取参数值
            tolerance = self.tolerance.get()
            feather = self.feather.get()  
            edge_clarity = self.edge_clarity.get()

            self.root.after(0, lambda: self.show_progress(f"{prefix}正在处理图片，请稍候..."))

            # ---------------- 计算 alpha 通道 ----------------
            if hasattr(self, "alg_mode_var") and self.alg_mode_var.get() == "flood":
                rgba_img = cv2.cvtColor(rgb_img, cv2.COLOR_RGB2RGBA)
                final_alpha = floodfill_alpha(rgba_img, background_colors,
                                              tolerance, feather, edge_clarity)
            else:
                final_alpha = distance_alpha(rgb_img, background_colors,
                                             tolerance, feather, edge_clarity)
            # ------------------------------------------------

            # 创建带透明通道的输出图像
            result_img = np.zeros((height, width, 4), dtype=np.uint8)
            result_img[:, :, 0:3] = rgb_img
            result_img[:, :, 3] = final_alpha

            # --------- 新增：裁剪去透明边 (可选) ---------
            if self.auto_crop_var.get():
                coords = np.column_stack(np.where(final_alpha > 0))
                if coords.size > 0:
                    y_min, x_min = coords.min(axis=0)
                    y_max, x_max = coords.max(axis=0)
                    result_img = result_img[y_min:y_max + 1, x_min:x_max + 1, :]
            # -------------------------------------------

            mode = self.bg_mode_var.get()
            if mode == "transparent":
                pil_img = Image.fromarray(result_img)
            else:
                # 前景
                fg_pil = Image.fromarray(result_img)
                if mode == "solid":
                    bg_pil = Image.new('RGBA', fg_pil.size, self.bg_color + (255,))
                else:  # image
                    if not self.bg_image_path or not os.path.exists(self.bg_image_path):
                        self.root.after(0, lambda: self._handle_error("请先选择背景图片"))
                        return
                    try:
                        bg_pil = Image.open(self.bg_image_path).convert('RGBA').resize(fg_pil.size, Image.LANCZOS)
                    except Exception as e:
                        self.root.after(0, lambda: self._handle_error(f"读取背景图片出错：{str(e)}"))
                        return
                composite = Image.alpha_composite(bg_pil, fg_pil)
                # 对于有背景的情况，直接输出RGB
                pil_img = composite.convert('RGB')

            # 根据是否批量决定保存方式
            if self.batch_save_dir:
                prefix_name = "transparent_" if mode == "transparent" else "output_"
                save_path = os.path.join(self.batch_save_dir, f"{prefix_name}{os.path.basename(img_path)}")
                try:
                    pil_img.save(save_path, "PNG")
                except Exception as e:
                    self.root.after(0, lambda: self._handle_error(f"保存文件时出错：{str(e)}"))
            else:
                # 单张或未设置批量目录时，弹出保存对话框
                self.root.after(0, lambda p=pil_img, op=img_path, m=mode: self._save_image(p, op, m))

        except Exception as e:
            self.root.after(0, lambda: self._handle_error(str(e)))

    def _save_image(self, pil_img, orig_path=None, mode="transparent"):
        """在主线程中保存图片 (重载增加 mode)"""
        try:
            self.hide_progress()

            default_name = "transparent_image.png" if mode == "transparent" else "output_image.png"
            # 保存文件
            save_path = filedialog.asksaveasfilename(
                defaultextension=".png",
                filetypes=[("PNG files", "*.png")],
                initialfile=default_name
            )

            if save_path:
                self.show_progress("正在保存文件...")
                # 在新线程中保存文件
                def save_thread():
                    try:
                        pil_img.save(save_path, "PNG")
                        self.root.after(0, lambda: self._save_complete())
                    except Exception as e:
                        self.root.after(0, lambda: self._handle_error(f"保存文件时出错：{str(e)}"))

                threading.Thread(target=save_thread, daemon=True).start()
            else:
                self.hide_progress()

        except Exception as e:
            self._handle_error(f"保存图片时出错：{str(e)}")

    def _save_complete(self):
        """保存完成"""
        self.hide_progress()
        messagebox.showinfo("成功", "图片处理完成！")

    def _handle_error(self, error_msg):
        """处理错误"""
        self.hide_progress()
        messagebox.showerror("错误", f"处理图片时出错：{error_msg}")

    def update_tolerance_value(self, value):
        """更新容差值显示"""
        self.tolerance_value_label.config(text=str(int(float(value))))

    def update_feather_value(self, value):
        """更新羽化值显示"""
        self.feather_value_label.config(text=str(int(float(value))))

    def update_edge_value(self, value):
        """更新边缘清晰度值显示"""
        self.edge_value_label.config(text=str(int(float(value))))

    def on_window_resize(self, event):
        """处理窗口大小变化事件"""
        # 获取窗口的实际大小
        window_width = self.root.winfo_width()
        window_height = self.root.winfo_height()

        # 只有当窗口大小有效时才处理
        if window_width > 100 and window_height > 100:
            # 调整内容框架大小
            self.content_frame.configure(width=window_width - 40)

            # 更新所有组件
            self.content_frame.update_idletasks()

    def _process_images_thread(self):
        """批量处理多张图片"""
        total = len(self.image_paths)
        for idx, img_path in enumerate(self.image_paths, start=1):
            self._process_single_image(img_path, idx, total)

        # 全部完成后
        self.root.after(0, lambda: self.hide_progress())
        self.root.after(0, lambda: messagebox.showinfo("成功", "所有图片已处理完成！"))

    def on_bg_mode_change(self):
        """根据背景模式显示/隐藏相关控件"""
        mode = self.bg_mode_var.get()
        # 先隐藏全部
        self.choose_color_btn.pack_forget()
        self.sample_bg_btn.pack_forget()
        self.choose_image_btn.pack_forget()
        if mode == "solid":
            self.choose_color_btn.pack(side=tk.LEFT, padx=5, before=self.detect_frame)
            self.sample_bg_btn.pack(side=tk.LEFT, padx=5, before=self.detect_frame)
        elif mode == "image":
            self.choose_image_btn.pack(side=tk.LEFT, padx=5, before=self.detect_frame)

    def choose_bg_color(self):
        """弹出颜色选择器"""
        color = colorchooser.askcolor(color=self.bg_color)
        if color and color[0]:
            # color[0] 是 (r, g, b)
            self.bg_color = tuple(int(c) for c in color[0])

    def choose_bg_image(self):
        """选择背景图片"""
        path = filedialog.askopenfilename(
            title="选择背景图片",
            filetypes=[
                ("所有支持的图片", "*.png *.jpg *.jpeg *.bmp *.gif *.tiff *.webp"),
                ("PNG文件", "*.png"),
                ("JPEG文件", "*.jpg *.jpeg"),
                ("BMP文件", "*.bmp"),
                ("GIF文件", "*.gif"),
                ("TIFF文件", "*.tiff *.tif"),
                ("WebP文件", "*.webp")
            ]
        )
        if path:
            self.bg_image_path = path

    # ---------------- 背景识别模式变更 ----------------
    def on_detect_mode_change(self):
        """根据模式启用/禁用取样按钮"""
        if self.detect_mode_var.get() == "sample" and self.image_paths:
            self.pick_color_btn.config(state=tk.NORMAL)
        else:
            self.pick_color_btn.config(state=tk.DISABLED)

    # ---------------- 取样颜色 ----------------
    def open_color_picker(self):
        """打开预览窗口供用户框选背景区域"""
        if self.detect_mode_var.get() != "sample" or not self.image_paths:
            return

        img_path = self.image_paths[0]
        try:
            img = Image.open(img_path).convert("RGB")
        except Exception as e:
            messagebox.showerror("错误", f"无法打开图片进行取色：{str(e)}")
            return

        # 缩放，最长边 ≤800
        max_size = 800
        ratio = min(max_size / img.width, max_size / img.height, 1)
        disp_w, disp_h = int(img.width * ratio), int(img.height * ratio)
        display_img = img.resize((disp_w, disp_h), Image.LANCZOS)

        self._picker_ratio = ratio
        self._picker_source_img = img  # 保存原图

        picker = tk.Toplevel(self.root)
        picker.title("拖拽鼠标框选背景区域")

        canvas = tk.Canvas(picker, width=disp_w, height=disp_h)
        canvas.pack()
        tk_img = ImageTk.PhotoImage(display_img)
        canvas.create_image(0, 0, anchor=tk.NW, image=tk_img)
        canvas.image = tk_img  # 防止垃圾回收

        # 记录拖拽
        self._drag_start = None
        self._current_rect = None  # 当前拖拽中的矩形
        self._rect_ids = []  # 已确认的矩形

        def on_press(ev):
            # 开始新的拖拽，创建临时矩形(红虚线)
            self._drag_start = (ev.x, ev.y)
            self._current_rect = canvas.create_rectangle(ev.x, ev.y, ev.x, ev.y,
                                                         outline="red", dash=(2,), width=2)

        def on_drag(ev):
            if not self._drag_start:
                return
            x0, y0 = self._drag_start
            if self._current_rect:
                canvas.coords(self._current_rect, x0, y0, ev.x, ev.y)

        def on_release(ev):
            if not self._drag_start:
                return
            x0, y0 = self._drag_start
            x1, y1 = ev.x, ev.y
            self._drag_start = None

            left, top = min(x0, x1), min(y0, y1)
            right, bottom = max(x0, x1), max(y0, y1)
            if right - left < 5 or bottom - top < 5:
                return  # 过小

            # 转原图坐标
            orig_left = int(left / self._picker_ratio)
            orig_top = int(top / self._picker_ratio)
            orig_right = int(right / self._picker_ratio)
            orig_bottom = int(bottom / self._picker_ratio)

            region = self._picker_source_img.crop((orig_left, orig_top, orig_right, orig_bottom))
            pixels = list(region.getdata())
            if not pixels:
                return

            unique_cols = set(pixels)
            added = 0
            for col in unique_cols:
                if col not in self.manual_bg_colors:
                    self.manual_bg_colors.append(col)
                    added += 1

            # 把临时矩形转为绿色实线，保存到列表
            if self._current_rect:
                canvas.itemconfig(self._current_rect, outline="green", dash=())
                self._rect_ids.append(self._current_rect)
                self._current_rect = None

            messagebox.showinfo("已取色", f"已选取 {len(pixels)} 像素\n新增颜色 {added} 种，候选颜色总数 {len(self.manual_bg_colors)}\n可继续拖拽更多区域，关闭窗口后开始处理。")

        canvas.bind("<ButtonPress-1>", on_press)
        canvas.bind("<B1-Motion>", on_drag)
        canvas.bind("<ButtonRelease-1>", on_release)

    def reset_samples(self):
        """清空已有的手动取样颜色，并提示用户"""
        self.manual_bg_colors = []
        messagebox.showinfo("已重置", "已清空所有手动取样颜色，可重新取样。")

    # ---------------- 取样输出背景颜色 ----------------
    def sample_bg_color(self):
        """让用户通过拖拽在任意图片中取平均颜色作为输出背景"""
        # 选择参考图片
        img_path = filedialog.askopenfilename(
            title="选择参考图片",
            filetypes=[
                ("常见图片格式", "*.png *.jpg *.jpeg *.bmp *.gif *.tiff *.webp"),
                ("PNG文件", "*.png"),
                ("JPEG文件", "*.jpg *.jpeg"),
                ("BMP文件", "*.bmp"),
                ("GIF文件", "*.gif"),
                ("TIFF文件", "*.tiff *.tif"),
                ("WebP文件", "*.webp"),
            ]
        )
        if not img_path:
            return

        try:
            img = Image.open(img_path).convert("RGB")
        except Exception as e:
            messagebox.showerror("错误", f"无法打开图片：{str(e)}")
            return

        # 创建取样窗口
        max_size = 800
        ratio = min(max_size / img.width, max_size / img.height, 1)
        disp_w, disp_h = int(img.width * ratio), int(img.height * ratio)
        display_img = img.resize((disp_w, disp_h), Image.LANCZOS)

        picker = tk.Toplevel(self.root)
        picker.title("拖拽鼠标框选区域以取背景颜色 (平均值)")

        canvas = tk.Canvas(picker, width=disp_w, height=disp_h)
        canvas.pack()
        tk_img = ImageTk.PhotoImage(display_img)
        canvas.create_image(0, 0, anchor=tk.NW, image=tk_img)
        canvas.image = tk_img

        drag_start = {"pt": None}
        current_rect = {"id": None}

        def on_press(ev):
            drag_start["pt"] = (ev.x, ev.y)
            if current_rect["id"]:
                canvas.delete(current_rect["id"])
            current_rect["id"] = canvas.create_rectangle(ev.x, ev.y, ev.x, ev.y, outline="red", dash=(2,), width=2)

        def on_drag(ev):
            if not drag_start["pt"]:
                return
            x0, y0 = drag_start["pt"]
            canvas.coords(current_rect["id"], x0, y0, ev.x, ev.y)

        def on_release(ev):
            if not drag_start["pt"]:
                return
            x0, y0 = drag_start["pt"]
            x1, y1 = ev.x, ev.y
            drag_start["pt"] = None

            left, top = min(x0, x1), min(y0, y1)
            right, bottom = max(x0, x1), max(y0, y1)
            if right - left < 5 or bottom - top < 5:
                return

            # 计算平均颜色
            orig_left = int(left / ratio)
            orig_top = int(top / ratio)
            orig_right = int(right / ratio)
            orig_bottom = int(bottom / ratio)

            region = img.crop((orig_left, orig_top, orig_right, orig_bottom))
            arr = np.array(region).reshape(-1, 3)
            mean_color = tuple(int(c) for c in arr.mean(axis=0))

            self.bg_color = mean_color

            canvas.itemconfig(current_rect["id"], dash=(), outline="green")

            messagebox.showinfo("已取色", f"已设置背景颜色为: {mean_color}\n可继续拖拽其他区域，或关闭窗口确认。")

        canvas.bind("<ButtonPress-1>", on_press)
        canvas.bind("<B1-Motion>", on_drag)
        canvas.bind("<ButtonRelease-1>", on_release)


if __name__ == "__main__":
    # 启用DPI感知
    try:
        from ctypes import windll

        windll.shcore.SetProcessDpiAwareness(1)
    except:
        pass  # 如果失败，继续执行

    root = ttk.Window()

    # 设置窗口初始大小
    root.geometry("670x720")  # 增加窗口高度以适应进度条

    # 设置最小窗口大小 - 设置较小的最小尺寸以允许更灵活的调整
    root.minsize(500, 600)

    app = TransparencyTool(root)

    # 更新窗口以确保所有组件都已正确布局
    root.update()

    # 获取内容的实际大小
    required_width = max(app.main_frame.winfo_reqwidth(), 670)
    required_height = max(app.main_frame.winfo_reqheight(), 680)

    # 调整窗口大小以适应内容
    root.geometry(f"{required_width}x{required_height}")

    # 允许窗口调整大小
    root.resizable(True, True)

    # 配置行列权重，使组件可以随窗口大小调整
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)

    root.mainloop()
